﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SilverChart.Core.Service;

namespace SilverChart.Core.Configuration.Converter {
    [ServiceProvider(LayzeCreation = true, Singleton = true)]
    public class BrushConverter : ConfigurationConverter<Brush> {

        //tolerant color pattern, try to use all color converters to convert color
        const String COLOR_REGEX = @"\S+"; //ColorConverter.COLOR + "|" + NamedColorConverter.COLOR;
        //float pattern, only 0, 0.xxxx.., 1.0000.., 1
        const String FLOAT_REGEX = @"0(.\d+)?|1(.0+)?";

        //float,float-float,float color,float color,float...
        const String GRADIENT_COMPONENTS = @"(?<START_X>" + FLOAT_REGEX + @"),(?<START_Y>" + FLOAT_REGEX + @")-(?<END_X>" + FLOAT_REGEX + @"),(?<END_Y>" + FLOAT_REGEX + @")" +
            @"(\s+(?<STOP_COLOR>" + COLOR_REGEX + @"),(?<STOP_POS>" + FLOAT_REGEX + @")){2,}";

        //[s/S ]color
        const String SOLID_BRUSH_REGEX = @"(?<SOLID>([sS]\s+)?(?<COLOR>" + COLOR_REGEX + "))";
        //l/L components
        const String LINE_GRADIENT_BRUSH_REGEX = "(" + @"?<LINE>([lL]\s+)?" + GRADIENT_COMPONENTS + ")";
        //r/R float float components
        const String RADIAL_GRADIENT_BRUSH_REGEX = "(" + @"?<RADIAL>([rR]?\s+)?(?<RADIUS_X>" + FLOAT_REGEX + @")\s+(?<RADIUS_Y>" + FLOAT_REGEX + @")\s+" + GRADIENT_COMPONENTS + ")";
        //uri(....)
        const String IMAGE_BRUSH_REGEX = @"(?<IMAGE>uri\((?<URI>.+)\))";

        public const String BRUSH = LINE_GRADIENT_BRUSH_REGEX + "|" +
            RADIAL_GRADIENT_BRUSH_REGEX + "|" + IMAGE_BRUSH_REGEX + "|" + SOLID_BRUSH_REGEX;

        const String FULL_BRUSH_REGEX = "^" + BRUSH + "$";

        private static Regex Brush_Regex_Object = new Regex(FULL_BRUSH_REGEX);

        public bool Convert(string input, out Brush value) {
            if (input != null && input.Trim().Length > 0) {
                Match match = Brush_Regex_Object.Match(input);
                if (match.Success) {
                    Brush brush = null;
                    try {
                        if (match.Groups["SOLID"].Success) {
                            brush = CreateSolidBrush(match);
                        }
                        else if (match.Groups["LINE"].Success) {
                            brush = CreateLineBrush(match);
                        }
                        else if (match.Groups["RADIAL"].Success) {
                            brush = CreateRadialBrush(match);
                        }
                        else if (match.Groups["IMAGE"].Success) {
                            brush = CreateImagelBrush(match);
                        }
                        value = brush;
                        return true;
                    }
                    catch {
                    }
                }
            }

            value = null;
            return false;
        }

        private Brush CreateImagelBrush(Match match) {
            ImageBrush brush = new ImageBrush();
            brush.ImageSource = new BitmapImage(new Uri(match.Groups["URI"].Value, UriKind.RelativeOrAbsolute));
            return brush;
        }

        private Brush CreateRadialBrush(Match match) {
            RadialGradientBrush brush = new RadialGradientBrush();

            String CenterX = match.Groups["START_X"].Value;
            String CenterY = match.Groups["START_Y"].Value;
            String GradientOriginX = match.Groups["END_X"].Value;
            String GradientOriginY = match.Groups["END_Y"].Value;

            brush.RadiusX = ConvertDouble(match.Groups["RADIUS_X"].Value);
            brush.RadiusY = ConvertDouble(match.Groups["RADIUS_Y"].Value);
            brush.Center = new Point(ConvertDouble(CenterX), ConvertDouble(CenterY));

            brush.GradientOrigin = new Point(ConvertDouble(GradientOriginX), ConvertDouble(GradientOriginY));

            ProcessGradientBrushStops(match, brush);

            return brush;
        }

        private Brush CreateLineBrush(Match match) {
            LinearGradientBrush brush = new LinearGradientBrush();

            String Start_X = match.Groups["START_X"].Value;
            String Start_Y = match.Groups["START_Y"].Value;
            String End_X = match.Groups["END_X"].Value;
            String End_Y = match.Groups["END_Y"].Value;
            Point StartPoint = new Point(ConvertDouble(Start_X), ConvertDouble(Start_Y));
            Point EndPoint = new Point(ConvertDouble(End_X), ConvertDouble(End_Y));
            brush.StartPoint = StartPoint;
            brush.EndPoint = EndPoint;

            ProcessGradientBrushStops(match, brush);

            return brush;
        }

        private void ProcessGradientBrushStops(Match match, GradientBrush brush) {
            for (int i = 0; i < match.Groups["STOP_COLOR"].Captures.Count; i++) {
                GradientStop stop = new GradientStop();
                stop.Color = ConvertColor(match.Groups["STOP_COLOR"].Captures[i].Value);
                stop.Offset = ConvertDouble(match.Groups["STOP_POS"].Captures[i].Value);
                brush.GradientStops.Add(stop);
            }
        }

        private Brush CreateSolidBrush(Match match) {
            SolidColorBrush brush = new SolidColorBrush();
            String color = match.Groups["COLOR"].Value;
            brush.Color = ConvertColor(color);
            return brush;
        }

        private Color ConvertColor(String color) {
            IList<ConfigurationConverter<Color>> converters = ServiceManager.Instance.GetServices<ConfigurationConverter<Color>>();
            Color c;
            foreach (ConfigurationConverter<Color> converter in converters) {
                if (converter.Convert(color, out c)) {
                    return c;
                }
            }
            throw new ArgumentException(color + " is not a color");
        }

        private Double ConvertDouble(String f) {
            return Double.Parse(f);
        }
    }
}
